Acknowledgement

Introducing Shiny

Shiny

  • Shiny is a framework for creating web applications using R code.
  • You can create pretty complicated Shiny apps with no knowledge of HTML, CSS, or JavaScript.

This workshop is for you if you are

  • comfortable with the basics of R, such as writing functions, indexing vectors and lists, debugging simple errors, and working with data structures like data frames

  • interested in creating interactive web applications, and

  • have no or minimal experience with Shiny for R.

If you have a bit of experience, you’ll see things in a new way. If you don’t, we’ll get you started on the right footing.

App Anatomy


Server

+


Client / Browser

+ +

App Components

library(shiny)

ui <- list()

server <- function(input, output, session) {}

shinyApp(ui = ui, server = server)

User Interface (UI)

UI - Layout

  • fluidPage() creates the most basic layout
    • display that automatically adjusts to user’s browser dimensions (smartphone!)
    • layout consists of rows which in turn include columns
      • rows making sure elements appear on the same line
      • columns define usage of horizontal space (within a 12-unit wide grid)

UI functions are HTML

  • One of the neat tricks of Shiny is that the interface is just a web page
    • and this can be seen by the fact that UI functions are just R functions that generate HTML.
  • We can run any of the following in our console and see the HTML output generated:
fluidPage()
sidebarLayout(sidebarPanel(), mainPanel())
radioButtons("test", "Test", choices = c("A", "B", "C"))

Multi-row layout


Other layouts

UI with title panel and sidebar panel

  • titlePanel() and sidebarLayout() create a basic Shiny app/layout with a sidebar
    • sidebarLayout() takes two functions
      • sidebarPanel(): Includes content displayed in the sidebar
      • mainPanel(): Includes content displayed in the main panel
  • fluidRow() and column(): Divide UI into rows/columns
Code: Creating a simple UI
ui <- fluidPage(
  titlePanel("This is the title panel"),
  sidebarLayout(
    sidebarPanel("Title: Sidebar panel"),
    mainPanel(
      "Title: Main panel",
      fluidRow(
        column(width = 4, "Column 1: Description here"),
        column(width = 4, "Column 2: Model summary"),
        column(
          width = 3,
          offset = 1,
          "Column 3: Model visualization"
        )
      )
    )
  )
)

server <- function(input, output, session) {}

shinyApp(ui = ui, server = server)

Creating panels and tabs

  • tabsetPanel() + tabPanel()
    • Allows for using sidebar layout but dividing main panel into tabs
    • Users can switch between tabs that show different outputs
ui <- fluidPage(
  titlePanel("This is the title panel"),
  sidebarLayout(
    sidebarPanel("This is the sidebar panel"),
    mainPanel(
      tabsetPanel(
        type = "tabs",
        tabPanel(
          "3d Frequency Plot",
          "Tab 1: plot here"
        ),
        tabPanel(
          "Histogram",
          "Tab 2: another plot here"
        ),
        tabPanel(
          "Model Summary",
          h4("Tab 3: estimation results here")
        ),
        tabPanel(
          "Data Summary",
          h4("Tab 4: Variable summaries")
        )
      )
    )
  )
)

server <- function(input, output, session) {}

shinyApp(ui = ui, server = server)

Input Widgets

UI - Inputs (Common Structure)

  • textInput(inputId = "username", label = "Enter your username", value = "Write here")
    • inputId connects front end with back end, e.g., if UI has input with ID name, the server function will access it with input$name
    • name = simple string (only letters, numbers, and underscores) and unique
    • label argument: used to create human-readable label
    • value argument: default value
  • Inputs are stored in list called input$...
  • Remaining 4+ arguments are unique to the particular input
  • Recommendation: Supply inputId and label arguments by position, and all other arguments by name
  • Q: How would we read the following?
    • sliderInput("min", "Limit (minimum)", value = 50, min = 0, max = 100)

UI Inputs: Logic

  • Widget = Web element the user can interact with (Shiny widget gallery)
    • Users can send messages to the SERVER/Computer (e.g. “I want to choose this variable”)
  • Underlying logic is the same for all widgets
    • User uses widget to give input
    • Input is inserted into the functions in the SERVER
      • server <- function(input, output, session) {}

Your Turn: UI Inputs

  • Read the code below. Can you guess what kind of inputs the various input functions create?
animals <- c("dog", "cat", "mouse") # Predefining some categories

ui <- fluidPage(

  # Free text
  textInput("name", "What's your name?"),
  textInput(inputId = "username", label = "Enter your username", placeholder = "Write here"),
  passwordInput("password", "What's your password?"),
  textAreaInput("story", "Tell me about yourself", rows = 3),

  # Numeric inputs
  numericInput("num", "Number one", value = 0, min = 0, max = 100),
  sliderInput("num2", "Number two", value = 50, min = 0, max = 100),
  sliderInput("rng", "Range", value = c(10, 20), min = 0, max = 100),

  # Dates
  dateInput("dob", "When were you born?"),
  dateRangeInput("holiday", "When do you want to go on vacation next?"),

  # Limited choices
  selectInput("state", "What's your favourite animal?", animals),
  radioButtons("animal", "What's your favourite animal?", animals),
  selectInput("state", "What's your favourite animal?", animals, multiple = TRUE),
  checkboxGroupInput("animal2", "What animals do you like?", animals),

  # Single checkbox
  checkboxInput("cleanup", "Clean up?", value = TRUE),
  checkboxInput("shutdown", "Shutdown?"),

  # File uploads
  fileInput("upload", NULL),

  # Action buttons
  actionButton("click", "Click me!"),
  actionButton("drink", "Drink me!", icon = icon("cocktail"))
)

server <- function(input, output, session) {}

shinyApp(ui, server)

Outputs

UI - Outputs

  • Outputs in UI create placeholders that are later filled by the server function
  • Have unique ID as first argument like inputs
    • e.g., textOutput("text") as ID text that is filled by the server
  • If UI specification creates an output with ID text, you’ll access it in the server function with output$text (see below)
  • Each output function on the front end is coupled with a render function in the back end (server)
  • Three main types of output: text, tables, and plots

Text output

  • Below an example for text output
ui <- fluidPage(
  textOutput("text"),
  verbatimTextOutput("code")
)
server <- function(input, output, session) {
  output$text <- renderText({
    "Hello friend!"
  })
  output$code <- renderPrint({
    summary(1:10)
  })
}
shinyApp(ui, server)

Table output

  • Below an example for table output
ui <- fluidPage(
  tableOutput("static"),
  DT::DTOutput("dynamic")
)
server <- function(input, output, session) {
  output$static <- renderTable(head(mtcars))
  output$dynamic <- DT::renderDT(mtcars, options = list(pageLength = 5))
}
shinyApp(ui, server)

dataTableOutput() vs DT::DTOutput()

Plot output

  • Below an example for plot output
ui <- fluidPage(
  plotOutput("plot", width = "400px")
)
server <- function(input, output, session) {
  output$plot <- renderPlot(plot(1:5), res = 96)
}
shinyApp(ui, server)

US Airport Weather data

  • We need some data that we will be able to interact with for our Shiny apps today.

  • Data are collected from Meteostat’s bulk data API for international airports in the lower 48 states.

readr::read_csv(here::here("data/weather.csv"))
# A tibble: 28,594 × 17
      id name  airport_code country state region longitude latitude date      
   <dbl> <chr> <chr>        <chr>   <chr> <chr>      <dbl>    <dbl> <date>    
 1 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-01
 2 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-02
 3 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-03
 4 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-04
 5 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-05
 6 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-06
 7 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-07
 8 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-08
 9 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-09
10 72202 Miami KMIA         US      FL    South      -80.3     25.8 2020-01-10
# ℹ 28,584 more rows
# ℹ 8 more variables: temp_avg <dbl>, temp_min <dbl>, temp_max <dbl>,
#   precip <dbl>, snow <dbl>, wind_direction <dbl>, wind_speed <dbl>,
#   air_press <dbl>

Demo 01 - Our first app

demos/demo01.R

library(tidyverse)
library(shiny)

d <- readr::read_csv(here::here("data/weather.csv"))

ui <- fluidPage(
  titlePanel("Temperatures at Major Airports"),
  sidebarLayout(
    sidebarPanel(
      radioButtons(
        "name", "Select an airport",
        choices = c(
          "Seattle-Tacoma",
          "Raleigh-Durham",
          "Houston Intercontinental",
          "Denver",
          "Los Angeles",
          "John F. Kennedy"
        )
      )
    ),
    mainPanel(
      plotOutput("plot")
    )
  )
)

server <- function(input, output, session) {
  output$plot <- renderPlot({
    d |>
      filter(name %in% input$name) |>
      ggplot(aes(x = date, y = temp_avg)) +
      geom_line() +
      theme_minimal()
  })
}

shinyApp(ui = ui, server = server)

Your turn - setup

usethis::create_from_github(
  "https://github.com/OWNER/REPO",
  destdir = "~/path/to/where/you/want/the/local/repo/",
  fork = FALSE
)

Your turn - Exercise 01

  • Open exercises/ex01.R and execute it via the Run App button in RStudio.

  • Check that you are able successfully run the shiny app and are able to interact with it by picking a new airport.

  • If everything is working try modifying the code, e.g. try adding or removing a city from radioButtons().

  • What happens if you add a city that is not in the weather.csv data set to the radio button input?

05:00

Troubleshooting

Do you have the latest versions of shiny and tidyverse installed?

Your turn - Exercise 02

  • We’ve just seen a number of alternative input widgets, starting from the code in exercises/ex02.R try changing the radioButton() input to something else.

What happens if you use an input capable of selecting multiple values, e.g.,

  • checkboxGroupInput()

  • or selectInput() with multiple = TRUE

10:00

plotly::ggplotly()

  • plotly::ggplotly() is a function that converts a ggplot object into a plotly object.

  • This allows you to create interactive plots from ggplot objects.

demos/ex02_plotly.R

library(tidyverse)
library(shiny)

d <- readr::read_csv(here::here("data/weather.csv"))

ui <- fluidPage(
  titlePanel("Temperatures at Major Airports"),
  sidebarLayout(
    sidebarPanel(
      selectInput(
        "name", "Select an airport",
        choices = c(
          "Seattle-Tacoma",
          "Raleigh-Durham",
          "Houston Intercontinental",
          "Denver",
          "Los Angeles",
          "John F. Kennedy"
        ),
        selected = "Seattle-Tacoma",
        multiple = TRUE
      )
    ),
    mainPanel(
      plotlyOutput("plot")
    )
  )
)

server <- function(input, output, session) {
  output$plot <- renderPlotly({
    p <- d |>
      filter(name %in% input$name) |>
      ggplot(aes(x = date, y = temp_avg, color = name)) +
      geom_line() +
      theme_minimal() +
      labs(color = "") +
      theme(legend.position = "bottom")
    plotly::ggplotly(p)
  })
}

shinyApp(ui = ui, server = server)